#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_BBOX_H
#define LM_CORE_BBOX_H

#include "graphics.h"
#include "geometry.h"

class BBox
{
public:
	BBox() {
		pMin = Point3(INFINITY, INFINITY, INFINITY);
		pMax = Point3(-INFINITY, -INFINITY, -INFINITY);
	}
	BBox(const Point3 &p) : pMin(p), pMax(p) { }
	BBox(const Point3 &p1, const Point3 &p2) {
		pMin = Point3(min(p1.x, p2.x), min(p1.y, p2.y), min(p1.z, p2.z));
		pMax = Point3(max(p1.x, p2.x), max(p1.y, p2.y), max(p1.z, p2.z));
	}
	friend BBox Union(const BBox &b, const Point3 &p);
	friend BBox Union(const BBox &b1, const BBox &b2);
	bool Overlaps(const BBox &b) const {
		bool x = (pMax.x >= b.pMin.x) && (pMin.x <= b.pMax.x);
		bool y = (pMax.y >= b.pMin.y) && (pMin.y <= b.pMax.y);
		bool z = (pMax.z >= b.pMin.z) && (pMin.z <= b.pMax.z);
		return (x && y && z);
	}
	bool Inside(const Point3 &pt) const {
		return (pt.x >= pMin.x && pt.x <= pMax.x &&
			pt.y >= pMin.y && pt.y <= pMax.y &&
			pt.z >= pMin.z && pt.z <= pMax.z);
	}
	void Expand(float delta) {
		pMin -= Vector3(delta, delta, delta);
		pMax += Vector3(delta, delta, delta);
	}
	float SurfaceArea() const {
		Vector3 d = pMax - pMin;
		return 2.f * (d.x * d.y + d.x * d.z + d.y * d.z);
	}
	float Volume() const {
		Vector3 d = pMax - pMin;
		return d.x * d.y * d.z;
	}
	int MaximumExtent() const {
		Vector3 diag = pMax - pMin;
		if (diag.x > diag.y && diag.x > diag.z)
			return 0;
		else if (diag.y > diag.z)
			return 1;
		else
			return 2;
	}
	const Point3 &operator[](int i) const;
	Point3 &operator[](int i);
	Point3 Lerp(float tx, float ty, float tz) const {
		return Point3(::Lerp(tx, pMin.x, pMax.x), ::Lerp(ty, pMin.y, pMax.y),
			::Lerp(tz, pMin.z, pMax.z));
	}
	Vector3 Offset(const Point3 &p) const {
		return Vector3((p.x - pMin.x) / (pMax.x - pMin.x),
			(p.y - pMin.y) / (pMax.y - pMin.y),
			(p.z - pMin.z) / (pMax.z - pMin.z));
	}
	void BoundingSphere(Point3 *c, float *rad) const;
	bool IntersectP(const Ray &ray, float *hitt0 = NULL, float *hitt1 = NULL) const;

	bool operator==(const BBox &b) const {
		return b.pMin == pMin && b.pMax == pMax;
	}
	bool operator!=(const BBox &b) const {
		return b.pMin != pMin || b.pMax != pMax;
	}
#ifdef USE_OPENGL
	void RenderGL();
#endif
	Point3 pMin, pMax;
};

#endif	// LM_CORE_BBOX_H